Contents

import numpy as np
import plotly.graph_objects as go

# — Updated Constants for Accuracy
R_u    = 8.314462618    # J/(mol·K), universal gas constant
T_trip = 273.16         # K, triple-point temperature
P_trip = 611.657        # Pa, triple-point pressure

# Melting (solid → liquid) at 0 °C
# ΔH_fus = 333.55 kJ/kg × 0.01801528 kg/mol ≈ 6006 J/mol
H_melt = 6006           # J/mol
# Volume change on melting at 0 °C: –1.639 cm³/mol = –1.639e–6 m³/mol
V_melt = -1.639e-6      # m³/mol

# Vaporization (liquid → gas) at 100 °C
# ΔH_vap(100 °C) ≈ 40.657 kJ/mol
H_vap  = 40657          # J/mol
T_boil = 373.15         # K, boiling-point temperature at 1 atm
P_boil = 101325         # Pa, standard 1 atm

# Sublimation (solid → gas) at triple point:
# ΔH_sub ≈ ΔH_fus + ΔH_vap(0 °C) ≈ 6006 + (2.5e6 J/kg × 0.01801528 kg/mol) ≈ 51060 J/mol
H_sub  = 6006 + (2.5e6 * 0.01801528)  # ≈51060 J/mol

# Critical point
T_crit = 647.096       # K
P_crit = 22.064e6      # Pa

# — Temperature arrays
T_melt_arr = np.linspace(T_trip - 5, T_trip, 500)        # melting curve
T_vap1_arr = np.linspace(T_trip,    T_boil, 500)        # liquid→vap (0–1 atm)
T_vap2_arr = np.linspace(T_boil - 50, T_crit, 500)      # liquid→vap (1 atm–critical)
T_sub_arr  = np.linspace(T_trip - 50, T_trip, 500)      # sublimation curve

# — Compute pressures
P_melt_arr = P_trip + (H_melt / V_melt) * np.log(T_melt_arr / T_trip)
P_vap1_arr = P_trip * np.exp((H_vap / R_u) * (1/T_trip - 1/T_vap1_arr))
P_vap2_arr = P_boil * np.exp((H_vap / R_u) * (1/T_boil - 1/T_vap2_arr))
P_sub_arr  = P_trip * np.exp((H_sub / R_u) * (1/T_trip - 1/T_sub_arr))

# — Build figure
fig = go.Figure([
    go.Scatter(x=T_sub_arr, y=P_sub_arr, mode='lines',
               name='Sublimation', line=dict(color='green', width=4)),
    go.Scatter(x=T_melt_arr, y=P_melt_arr, mode='lines',
               name='Fusion', line=dict(color='black', width=4)),
    go.Scatter(x=T_vap1_arr, y=P_vap1_arr, mode='lines',
               name='Vapour (0–1 atm)',
               line=dict(color='blue', width=4, dash='dash')),
    go.Scatter(x=T_vap2_arr, y=P_vap2_arr, mode='lines',
               name='Vapour (1 atm–crit)',
               line=dict(color='blue', width=4)),
    go.Scatter(x=[T_trip], y=[P_trip], mode='markers+text', name='Triple Point',
               marker=dict(size=12, color='black'),
               text=['Triple Point'], textposition='bottom right'),
    go.Scatter(x=[T_crit], y=[P_crit], mode='markers+text', name='Critical Point',
               marker=dict(size=12, symbol='diamond', color='black'),
               text=['Critical Point'], textposition='top left'),
])

fig.update_layout(
    title='Water Phase Diagram (Clausius–Clapeyron)',
    xaxis=dict(title='Temperature (K)', range=[200, 700]),
    yaxis=dict(title='Pressure (Pa)', type='log'),
    width=800,
    height=600,
    legend=dict(y=0.5),
    template='plotly_white'
)

fig.show()
import numpy as np
import plotly.graph_objects as go
import ipywidgets as widgets
from IPython.display import display

# Constants
Rd = 287.05
g = 9.81
cp = 1005.0
Lv = 2.5e6
Rv = 461.5
epsilon = Rd / Rv

def gamma_m(T, q_s):
    return (g / cp) * (1 + (Lv * q_s) / (Rd * T)) / (1 + (Lv**2 * q_s) / (cp * Rv * T**2))

def saturation_mixing_ratio(T, p):
    es = 6.112 * np.exp((17.67 * (T - 273.15)) / (T - 29.65)) * 100
    return epsilon * es / (p - es)

def compute_profiles(T0, p0=100000, z_top=15000, dz=100):
    z = [0]
    T_dry = [T0]
    T_moist = [T0]
    p = [p0]
    while z[-1] < z_top and T_moist[-1] > 200:
        T_dry.append(T_dry[-1] - dz * g / cp)
        q_s = saturation_mixing_ratio(T_moist[-1], p[-1])
        gamma = gamma_m(T_moist[-1], q_s)
        T_moist.append(T_moist[-1] - dz * gamma)
        p.append(p[-1] * np.exp(-dz * g / (Rd * T_moist[-1])))
        z.append(z[-1] + dz)
    return np.array(z), np.array(T_dry), np.array(T_moist)

def plot_profiles(T0):
    z, T_dry, T_moist = compute_profiles(T0)
    fig = go.Figure()
    fig.add_trace(go.Scatter(x=T_dry, y=z, mode='lines', name='Dry Adiabat'))
    fig.add_trace(go.Scatter(x=T_moist, y=z, mode='lines', name='Moist Adiabat'))
    fig.update_layout(
        title='Dry vs Moist Adiabatic Profiles',
        xaxis_title='Temperature (K)',
        yaxis_title='Height (m)',
        yaxis=dict(autorange='reversed'),
        height=600
    )
    fig.show()

widgets.interact(plot_profiles, T0=widgets.FloatSlider(value=300, min=280, max=320, step=1, description='T₀ [K]'));
import numpy as np
import plotly.graph_objects as go
from plotly.subplots import make_subplots



# Constants
Rd = 287.05
g = 9.81
cp = 1005.0
Lv = 2.5e6
Rv = 461.5
epsilon = Rd / Rv

layers = [
    (0,     -0.0065, 288.15, 101325),    # Troposphere (0–11 km)
    (11000, 0.0,     216.65, 22632.1),   # Tropopause (11–20 km)
    (20000, 0.001,   216.65, 5474.89),   # Lower Stratosphere (20–32 km)
    (32000, 0.0028,  228.65, 868.02),    # Middle Stratosphere (32–47 km)
    (47000, 0.0,     270.65, 110.91),    # Stratopause (47–51 km)
    (51000, -0.0028, 270.65, 66.94),     # Mesosphere (51–71 km)
    (71000, -0.002,  214.65, 3.96),      # Upper Mesosphere (71–84.852 km)
    (84852, 0.0,     186.87, 0.3734)     # Above 84.852 km (constant T assumed)
]

# Generate altitude array
h = np.linspace(0, 100000, 100)
T = np.zeros_like(h)
P = np.zeros_like(h)

# Initialize
T[0] = 288.15
P[0] = 101325

# Iterate through layers
for i in range(1, len(h)):
    h_i = h[i]
    for base_h, lapse, base_T, base_P in reversed(layers):
        if h_i >= base_h:
            delta_h = h_i - base_h
            if lapse == 0:
                T[i] = base_T
                P[i] = base_P * np.exp(-g * delta_h / (R * base_T))
            else:
                T[i] = base_T + lapse * delta_h
                P[i] = base_P * (T[i] / base_T) ** (-g / (R * lapse))
            break

# Compute density from ideal gas law
rho = P / (R * T)
h_km = h / 1000

# Create 3 subplots
fig = make_subplots(
    rows=1, cols=3,
    shared_yaxes=True,
    horizontal_spacing=0.1,
    titles=("Temperature [K]", "Pressure [Pa]", "Density [kg/m³]")
)

# Add traces
fig.add_trace(go.Scatter(x=T, y=h_km, mode='lines', name='Temperature', line=dict(color='firebrick')), row=1, col=1)
fig.add_trace(go.Scatter(x=P, y=h_km, mode='lines', name='Pressure', line=dict(color='royalblue')), row=1, col=2)
fig.add_trace(go.Scatter(x=rho, y=h_km, mode='lines', name='Density', line=dict(color='green')), row=1, col=3)

# Update layout and reverse y-axis
fig.update_layout(
    title="US Standard Atmosphere up to 100 km",
    height=700,
    width=1100,
    showlegend=False,
)

fig.show()
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
Cell In[3], line 46
     44             else:
     45                 T[i] = base_T + lapse * delta_h
---> 46                 P[i] = base_P * (T[i] / base_T) ** (-g / (R * lapse))
     47             break
     49 # Compute density from ideal gas law

NameError: name 'R' is not defined
# Read Mauna Loa CO2 monthly means (auto-detect header after comments)
mauna_df = pd.read_csv(mauna_path, comment="#", header=0)
global_df = pd.read_csv(global_path, comment="#", header=0)

# Keep only numeric rows
mauna_df = mauna_df.dropna(subset=["year", "month", "average"])
global_df = global_df.dropna(subset=["year", "month", "average"])

# Create datetime columns
mauna_df["date"] = pd.to_datetime(dict(year=mauna_df.year.astype(int),
                                       month=mauna_df.month.astype(int),
                                       day=1))
global_df["date"] = pd.to_datetime(dict(year=global_df.year.astype(int),
                                        month=global_df.month.astype(int),
                                        day=1))

# Plot
fig = go.Figure()

fig.add_trace(go.Scatter(
    x=mauna_df["date"], y=mauna_df["average"],
    mode="lines", name="Mauna Loa CO₂ (ppm)",
    line=dict(color="firebrick", width=2)
))

fig.add_trace(go.Scatter(
    x=global_df["date"], y=global_df["average"],
    mode="lines", name="Global Mean CO₂ (ppm)",
    line=dict(color="royalblue", width=2, dash="dash")
))

fig.update_layout(
    title="Mauna Loa vs Global Mean CO₂ Concentrations",
    xaxis_title="Year",
    yaxis_title="CO₂ concentration (ppm)",
    hovermode="x unified",
    width=900, height=500,
    template="plotly_white",
    legend=dict(x=0.01, y=0.99, bordercolor="black", borderwidth=1)
)

# Save interactive HTML
output_html = "/mnt/data/co2_mauna_global.html"
fig.write_html(output_html)

output_html
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
Cell In[23], line 2
      1 # Read Mauna Loa CO2 monthly means (auto-detect header after comments)
----> 2 mauna_df = pd.read_csv(mauna_path, comment="#", header=0)
      3 global_df = pd.read_csv(global_path, comment="#", header=0)
      5 # Keep only numeric rows

NameError: name 'mauna_path' is not defined